
/**
 * Baijiahulian.com Inc. Copyright (c) 2014-2016 All Rights Reserved.
 */

package cn.wenhao.javaClassReload.constant;

import java.util.HashMap;
import java.util.Map;

import org.objectweb.asm.Type;

import cn.wenhao.javaClassReload.JavaClassModify.interfaces.ExecutorAccessor;
import cn.wenhao.javaClassReload.javaClassElements.ObjExecutor;
import lombok.extern.slf4j.Slf4j;

/**
 * @say little Boy, don't be sad.
 * @name Rezar
 * @time Oct 29, 2016
 * @Desc this guy is too lazy, nothing left.
 */
@Slf4j
public class ObjExecutorCache {

    private static final Map<String, ObjExecutor> objExecutors = new HashMap<>();

    private static final Map<String, ReloadMethodInvoker> reloadables = new HashMap<>();

    public static void registerObjExecutor(String hostClass, ObjExecutor objExecutor) {
        log.debug("hostClass:{} add objExecutor in cache : {} ", hostClass, objExecutor);
        objExecutors.put(hostClass, objExecutor);
    }

    public static ObjExecutor getObjExecutor(Object hostClass) {
        ObjExecutor ret = (ObjExecutor) objExecutors.get(Type.getInternalName(hostClass.getClass()));
        // log.debug("hostClass:{} and find ObjExecutor:{} ", hostClass, ret);
        ret = cloneObjExecutor(ret);
        ret.initMethodInvoker(hostClass);
        return ret;
    }

    /**
     * @param ret
     * @return
     */
    private static ObjExecutor cloneObjExecutor(ObjExecutor ret) {
        return ret;
    }

    /**
     * @param string
     * @param string2
     * @param name
     * @return
     */
    public static Object invokeMethod(Object host, String methodNameAndDesc, Object...arugs) {
        // log.debug("host : {} is instanceof ExecutorAccessor : {} ", host, (host instanceof ExecutorAccessor));
        ObjExecutor executor = null;
        if (host instanceof ExecutorAccessor) {
            ExecutorAccessor hostExecutor = (ExecutorAccessor) host;
            executor = hostExecutor.getObjMethodExecutor();
        } else if (host instanceof String) {
            String hostClass = (String) host;
            executor = objExecutors.get(hostClass);
        }
        ReloadMethodInvoker rmi = null;
        if ((rmi = reloadables.get(getReloadMark(host.getClass().getName(), methodNameAndDesc))) != null) {
            executor.replaceMethodInvoker(rmi.getNewClassName(), methodNameAndDesc, rmi.getNewMethodInvokerBytes());
        }
        Integer register = executor.register(methodNameAndDesc);
        return executor.invokeMethod(register, host, arugs);
    }

    /**
     * @param string
     * @param string2
     * @param invokeMethod
     * @param invokeMethod2
     */
    public static Object invokeStaticMethod(String hostClass, String methodNameAndDesc, Object...argus) {
        ObjExecutor executor = objExecutors.get(hostClass);
        Integer register = executor.register(methodNameAndDesc);
        return executor.invokeMethod(register, hostClass, argus);
    }

    /**
     * @param checkNameMethodNameAndDesc
     * @param readFromFile
     */
    public static void reloadableMethodInvoker(String hostClass, String newClassName, String methodNameAndDesc,
        byte[] newMethodInvokerBytes) {
        ReloadMethodInvoker rmi = new ReloadMethodInvoker(newClassName, methodNameAndDesc, newMethodInvokerBytes);
        String key = getReloadMark(hostClass, methodNameAndDesc);
        reloadables.put(key, rmi);
    }

    private static final String key_format = "%s:%s";

    private static String getReloadMark(String hostClass, String methodNameAndDesc) {
        return String.format(key_format, hostClass, methodNameAndDesc);
    }

}
